#include "APnMView.h"
#include <QtGui/QDragEnterEvent>
#include <QtGui/QDropEvent>
#include <QtGui/QDragMoveEvent>
#include <QtGui/QKeyEvent>
#include <QtGui/QPainterPath>
#include <QtCore/QMimeData>
#include <QtGui/QApplication>
#include <QtCore/QFile>
#include <QtGui/QMessageBox>
#include <QtGui/QFileDialog>
#include <QtGui/QInputDialog>
#include <QtGui/QStringListModel>
#include <QtGui/QListView>
#include <QtGui/QTextEdit>
#include <QtGui/QTableView>
#include <QtGui/QStandardItemModel>
#include <QtGui/QPrinter>
#include <QtGui/QPrintDialog>
#include <QtXml/QDomDocument>
#include <QtXml/QDomElement>
#include <QtCore/QMap>
#include <QtCore/QVector>
#include "APnMScene.h"
#include "gactiveelement.h"
#include "dactiveelement.h"
#include "gpool.h"
#include "dpool.h"
#include "link.h"
#include "itemmodel.h"
#include "DElementDelegate.h"

bool loadXmlFile(const QString & xmlFile, QDomDocument & doc);

APnMView::APnMView(QWidget *parent)
	: QGraphicsView(parent), mApp(parent)
{
	mScene = new APnMScene(this);
	setScene(mScene);
	connect(mScene, SIGNAL(selectionChanged()),
		    this, SLOT(selectionChanged()));

	mApp.setScene(mScene);
	connect(mScene, SIGNAL(addLink(Link*)), this, SLOT(addLink(Link*)));

	setAttribute(Qt::WA_DeleteOnClose);
	setAttribute(Qt::WA_AcceptDrops);
	isUntitled = true;

	mTokenMode = false;
	mRunMode = false;
}

APnMView::~APnMView()
{
}

void  APnMView::dragEnterEvent(QDragEnterEvent *event)
{
	if ( event->mimeData()->hasFormat( dragDataType) )
		event->acceptProposedAction();
}

void  APnMView::dropEvent( QDropEvent * event )
{
	if ( event->mimeData()->hasFormat( dragDataType ) )
	{
		QByteArray encoded = event->mimeData()->data( dragDataType );
		QDataStream stream(&encoded, QIODevice::ReadOnly);
		QPointF pos = mapToScene( event->pos() );
		while (!stream.atEnd()) {
			QString kind, className;
			stream >> kind >> className;

			// find its DElement in DElementDelegate
			DElement* droped = DElementDelegate::instance()->find(kind, className);

			GElement *element = droped->createGraphics();	// use the Visitor Pattern
			
			QPointF adjust = element->boundingRect().bottomRight(); 
			element->setPos( pos - adjust/2 );
			//mScene->addItem(item);	
			mApp.addElement(element);
			pos += element->boundingRect().bottomLeft() + QPointF(0,5); //add a little gap
		}
		event->acceptProposedAction();
		setWindowModified(true);
	}	
}
void APnMView::dragMoveEvent( QDragMoveEvent * event )
{
	if ( event->mimeData()->hasFormat( dragDataType) )
			event->acceptProposedAction();
}

bool APnMView::hasSelection()
{
	if ( mScene->selectedItems().empty() )
		return false;
	else
		return true;
}

void APnMView::selectionChanged()
{
	if ( hasSelection() )
		emit copyAvailable(true);
	else
		emit copyAvailable(false);
}

void APnMView::cut()
{

}

void APnMView::copy()
{

}

void APnMView::paste()
{

}

void APnMView::addLink(Link* newLink)
{
	mApp.addLink(newLink);
}

void APnMView::compositeUp()
{

}

void APnMView::compositeDown()
{
	QList<QGraphicsItem*> items = mScene->selectedItems();
	if (!items.isEmpty()) {
		GActiveElement* e = qgraphicsitem_cast<GActiveElement*>(items.first());
		if (e) {
			if (e->kind() == kComposite) {
				emit openFile(e->getSubFilePath());
			}
		}
	}
}


void APnMView::del()
{
	mApp.deleteItems( mScene->selectedItems() );

	selectionChanged();
	setWindowModified(true);
}

void APnMView::newFile()
{
	static int sequenceNumber = 1;

	isUntitled = true;
	curFile = tr("SciWfApp%1").arg(sequenceNumber++);
	setWindowTitle(curFile + "[*]");

	//connect(scene, SIGNAL( changed(const QList<QRectF> &) ),
	//	this, SLOT(appWasModified()));
}

bool APnMView::loadFile(const QString &fileName)
{
	QString impFile = fileName+".imp";
	QString layFile = fileName+".lay";

	QDomDocument impDoc, layDoc;

	if (!loadXmlFile(impFile, impDoc) || !loadXmlFile(layFile, layDoc)) {
		QMessageBox::warning(this, tr("XMLnotValid"),
			tr("Please make sure that the input files are valid XML files!"));
		return false;
	}

	QApplication::setOverrideCursor(Qt::WaitCursor);

	mApp.loadFromXml(impDoc, layDoc);

	QApplication::restoreOverrideCursor();
	setCurrentFile(fileName);
	
	return true;
}

bool APnMView::save()
{
	if (isUntitled) {
		return saveAs();
	} else {
		return saveFile(curFile);
	}
}

bool APnMView::saveAs()
{
	QString fileName = QFileDialog::getSaveFileName(this, tr("Save As"),
		curFile);
	if (fileName.isEmpty())
		return false;

	QString noSuffix = fileName;
	int index = noSuffix.lastIndexOf('.');
	if (index != -1) 
		noSuffix.truncate(noSuffix.lastIndexOf('.'));
	return saveFile(noSuffix);
}

bool APnMView::saveFile(const QString &fileName)
{
	QString impFileName = fileName+".imp";
	QString layFileName = fileName+".lay";
	QFile impFile(impFileName);
	if (!impFile.open(QFile::WriteOnly | QFile::Text)) {
		QMessageBox::warning(this, tr("APnModel"),
			tr("Cannot write file %1:\n%2.").arg(impFileName).arg(impFile.errorString())
			);
		return false;
	}
	QFile layFile(layFileName);
	if (!layFile.open(QFile::WriteOnly | QFile::Text)) {
		QMessageBox::warning(this, tr("APnModel"),
			tr("Cannot write file %1:\n%2.").arg(layFileName).arg(layFile.errorString())
			);
		return false;
	}

	QApplication::setOverrideCursor(Qt::WaitCursor);

	QXmlStreamWriter impWriter(&impFile), layWriter(&layFile);

	mApp.saveToXml(impWriter, layWriter);

	impFile.close();
	layFile.close();
	QApplication::restoreOverrideCursor();
	
	setCurrentFile(fileName);
	return true;
}

bool APnMView::print()
{
	QPrinter printer;
	QPrintDialog printDialog(&printer);
	if (printDialog.exec() == QDialog::Accepted) {
		QPainter painter(&printer);
		mScene->render(&painter); 
		return true;
	} 
	return false;
}

QString APnMView::userFriendlyCurrentFile()
{
	return strippedName(curFile);
}

void APnMView::closeEvent(QCloseEvent *event)
{
	if (maybeSave()) {
		event->accept();
	} else {
		event->ignore();
	}
}

bool APnMView::maybeSave()
{
	if (isWindowModified ()) {
		QMessageBox::StandardButton ret;
		ret = QMessageBox::warning(this, tr("APnModel-IDE"),
			tr("'%1' has been modified.\n"
			"Do you want to save your changes?")
			.arg(userFriendlyCurrentFile()),
			QMessageBox::Save | QMessageBox::Discard
			| QMessageBox::Cancel);
		if (ret == QMessageBox::Save)
			return save();
		else if (ret == QMessageBox::Cancel)
			return false;
	}
	return true;
}

void APnMView::setCurrentFile(const QString &fileName)
{
	//curFile = QFileInfo(fileName).canonicalFilePath();
	curFile = fileName;
	isUntitled = false;
	setWindowModified(false);
	setWindowTitle(userFriendlyCurrentFile() + "[*]");
}

QString APnMView::strippedName(const QString &fullFileName)
{
	return QFileInfo(fullFileName).fileName();
} 

//void APnMView::addTextItem()
//{ 
//	QGraphicsTextItem *item = mScene->addText("Test Editalbe"); 
//	item->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable );
//	item->setTextInteractionFlags( Qt::TextEditorInteraction );
//	item->setPos(100,100);
//}

void APnMView::keyPressEvent(QKeyEvent *event)
{
	switch (event->key()) {
	 case Qt::Key_A:
		 if (Qt::ControlModifier & event->modifiers()) {
			mApp.selectAll();
		 }
		 break;
	 case Qt::Key_F5:
		 mApp.updateAllLinks();
	 default:
		 QGraphicsView::keyPressEvent(event);
	}
}

void APnMView::contextMenuEvent ( QContextMenuEvent * event )
{
	//QGraphicsView::contextMenuEvent(event);
	scene()->clearSelection();
	QGraphicsItem* item = itemAt(event->pos());
	if (!item) return;

	item = item->topLevelItem();
	item->setSelected(true);

	GElement* ele = dynamic_cast<GElement*>(item);
	if (!ele) return;

	GActiveElement* activeEle = dynamic_cast<GActiveElement*>(ele);

	QMenu menu;
	QAction* changeNameAct = menu.addAction(tr("Change Name"));
	QAction* portNameAct;
	QAction* portInfoAct;
	if (activeEle) {
		portNameAct = menu.addAction(tr("Show Port Name"));
		portNameAct->setCheckable(true);
		portNameAct->setChecked(activeEle->portNameVisible());
		portInfoAct = menu.addAction(tr("Show Port Information"));
	}
	QAction* functionAct = menu.addAction(tr("Function"));
	QAction* semanticsAct = menu.addAction(tr("Ontologies"));
	QAction* patternsAct = menu.addAction(tr("Patterns"));
	QAction* examplesAct = menu.addAction(tr("Examples"));
	QAction* documentAct = menu.addAction(tr("Doucment"));

	QAction* act = menu.exec(QCursor::pos());
	//avoid to equal with portNameAct or portInfoAct
	if (act == NULL) 
		return;

	if(act == changeNameAct) 
		setElementName(ele);
	if(act == portNameAct)
		flipPortNameVisible(activeEle);
	if(act == portInfoAct)
		showPortInfo(activeEle);
	if(act == functionAct)
		showFunction(ele);
	if(act == semanticsAct)
		showSemantics(ele);
	if(act == patternsAct)
		showPatterns(ele);
	if(act == examplesAct)
		showExamples(ele);
	if (act == documentAct) 
		showDocument(ele);
}

void APnMView::setElementName(GElement* ele)
{
	bool ok;
	QString text = QInputDialog::getText(NULL, tr("Set the Element Name"),
		tr("Element Name:"), QLineEdit::Normal,
		ele->elementName(), &ok);
	if (ok && !text.isEmpty())
		ele->setElementName(text);
}

void APnMView::flipPortNameVisible(GActiveElement* activeEle)
{
	activeEle->flipPortNameVisible();
}

//All show should be implemented in a stand alone Widget
void APnMView::showPortInfo(GActiveElement* activeEle)
{
	DActiveElement* active = qobject_cast<DActiveElement*>(activeEle->description());
	QString name = active->displayName().append(tr(" PortInfo"));
	QTableView* view = findChild<QTableView*>(name);
	if (!view) {
		view = new QTableView(this);
		view->setObjectName(name);
		view->setWindowTitle(name);

		QStandardItemModel *model = new QStandardItemModel(0,6,view);
		QVector<DPort*> ports = active->ports();
		model->setHeaderData(0, Qt::Horizontal, tr("Name"));
		model->setHeaderData(1, Qt::Horizontal, tr("Type"));
		model->setHeaderData(2, Qt::Horizontal, tr("Direction"));
		model->setHeaderData(3, Qt::Horizontal, tr("Multiple"));
		model->setHeaderData(4, Qt::Horizontal, tr("Note"));
		model->setHeaderData(5, Qt::Horizontal, tr("Ordinal"));
		int row;
		for (row=0; row<ports.size(); ++row) {
			 model->insertRows(row, 1, QModelIndex());
			 model->setData(model->index(row, 0, QModelIndex()), ports[row]->name);
			 model->setData(model->index(row, 1, QModelIndex()), ports[row]->typeOf);
			 model->setData(model->index(row, 2, QModelIndex()), ports[row]->direction);
			 model->setData(model->index(row, 3, QModelIndex()), ports[row]->multiple);
			 model->setData(model->index(row, 4, QModelIndex()), ports[row]->note);
			 model->setData(model->index(row, 5, QModelIndex()), ports[row]->ordinal);
		}

		view->setModel(model);
		++row;
		view->resize(4*100, row*40);
	}
	view->setWindowFlags(Qt::Drawer);
	QPoint pos = view->pos();
	if (pos.x() < 0)
		pos.setX(0);
	if (pos.y() < 0)
		pos.setY(0);
	view->move(pos);
	view->show();
}

// All use Qt object hierarchy
void APnMView::showFunction(GElement* ele)
{
	DElement* dEle = ele->description();
	QString name = dEle->displayName().append(tr(" Function"));
	QTextEdit* text = findChild<QTextEdit*>(name);
	if (!text) {
		text = new QTextEdit(this);
		text->setObjectName(name);
		text->setWindowTitle(name);
		text->setText(dEle->funtion());
	}	
	text->setWindowFlags(Qt::Drawer);
	QPoint pos = text->pos();
	if (pos.x() < 0)
		pos.setX(0);
	if (pos.y() < 0)
		pos.setY(0);
	text->move(pos);
	text->show();
}

void APnMView::showSemantics(GElement* ele)
{
	DElement* dEle = ele->description();
	QString name = dEle->displayName().append(tr(" Semantics"));

	viewList(*dEle->semantics(), name);

}
void APnMView::showPatterns(GElement* ele)
{
	DElement* dEle = ele->description();
	QString name = dEle->displayName().append(tr(" Patterns"));

	viewList(*dEle->patterns(), name);
}
void APnMView::showExamples(GElement* ele)
{
	DElement* dEle = ele->description();
	QString name = dEle->displayName().append(tr(" Examples"));

	viewList(*dEle->examples(), name);
}

void APnMView::showDocument(GElement* ele)
{
}

void APnMView::viewList(const QStringList& data, const QString& name)
{
	QListView* view = findChild<QListView*>(name);
	if (!view) {
		view = new QListView(this);
		view->setObjectName(name);
		view->setWindowTitle(name);
		QStringListModel *model = new QStringListModel(data, view);
		view->setModel(model);
	}	
	view->setWindowFlags(Qt::Drawer);
	QPoint pos = view->pos();
	if (pos.x() < 0)
		pos.setX(0);
	if (pos.y() < 0)
		pos.setY(0);
	view->move(pos);
	view->show();
}

void APnMView::mousePressEvent( QMouseEvent * event )
{
	if (mTokenMode && (event->button()== Qt::LeftButton)) 
		if (QGraphicsItem *item = itemAt(event->pos())) 
			if (GElement *ele = dynamic_cast<GElement*>(item->topLevelItem())) {
				TokensInput* tokenEditor;
				QMap<QString, TokensInput*>::iterator it = mTokenEditors.find(ele->elementName());
				if (it == mTokenEditors.end()) {
					tokenEditor = new TokensInput(ele, this);
					mTokenEditors.insert(ele->elementName(), tokenEditor);
				}else
					tokenEditor = *it;
				
				tokenEditor->show();
				tokenEditor->activateWindow();
			}

	QGraphicsView::mousePressEvent(event);
}

bool APnMView::upload()
{
	static bool ok = false;

	Q_ASSERT(!mRunMode);

	ok = !ok;
	if (ok) 
		mRunMode = !mRunMode;

	return ok;
}

bool APnMView::terminate()
{
	static bool ok = false;

	Q_ASSERT(mRunMode);

	ok = !ok;
	if (ok) 
		mRunMode = !mRunMode;

	return ok;
}